#include <gtest/gtest.h>
#include <lcir/ADT/IList.h>
#include <lcir/ADT/ListNode.h>
#include <lcir/ADT/Range.h>
#include <dialects/Builtin/Builtin.h>
#include <dialects/Func/Func.h>
#include <dialects/PL/PL.h>
#include <lcir/IR/Argument.h>
#include <lcir/IR/Constant.h>
#include <lcir/IR/Context.h>
#include <lcir/IR/ExprTemps.h>
#include <lcir/IR/Expression.h>
#include <lcir/IR/IRBuilder.h>
#include <lcir/IR/IRRewritter.h>
#include <lcir/IR/Lambda.h>
#include <lcir/IR/LambdaProto.h>
#include <lcir/IR/LambdaType.h>
#include <lcir/IR/Op.h>
#include <lcir/IR/Prototype.h>
#include <lcir/IR/Slot.h>
#include <lcir/IR/SlotCombiner.h>
#include <lcir/IR/Type.h>
#include <lcir/IR/VReg.h>
#include <lcir/IR/ValuePrinter.h>

#include <iterator>
#include <memory>
#include <string>
#include <vector>

using namespace lcir;
using namespace lcir::builtin;
TEST(Builtin, Builtin) {
  auto *module = Lambda::get();
  IRBuilder builder(module);
  IRRewritter rewriter(module);
  auto *lambda = Lambda::get({builtin::Int32Type::get(), builtin::FloatType::get()}, nullptr);

  auto *oldTy = lambda->getType();

  auto *expr = builder.create<func::Func>({lambda}, "");
  builder.setInsertPoint(lambda);
  rewriter.setInsertPoint(lambda);
  auto *x = builder.createAndGetRet<pl::DeclVar>(Int32Type::get(), "x");
  auto *add = builder.create<pl::Add>({lambda->getArg(0), lambda->getArg(1)});
  auto *sub = builder.create<pl::Sub>({add->getResult(), add->getResult()});
  auto *assign = builder.create<pl::Assign>({x, add->getResult()});
  builder.create<pl::Assign>({lambda->getArg(0), x});
  builder.create<pl::Assign>({lambda->getArg(0), Constant::get(std::make_unique<Int32ConstProto>(2333))});
  ValuePrinter printer(module);
  lambda->addArg(builtin::Int32Type::get());
  auto *newTy = lambda->getType();
  std::cout << printer.print() << "\n";
  rewriter.replaceValue(add->getResult(), lambda->getArg(1));
  std::cout << printer.print() << "\n";
  EXPECT_NE(oldTy, newTy);
  auto binaryExpr = sub->getTemp<BinaryExpr>();
}

TEST(Builtin, IfEl) {
  // 创建模块
  auto *module = Lambda::get();
  IRBuilder builder(module);
  // Lambda参数 ({参数类型....}, 返回值类型)
  auto *funcLambda = Lambda::get({Int32Type::get(), FloatType::get()}, nullptr);
  // 添加函数的参数
  funcLambda->addArg(Int32Type::get());
  builder.create<func::Func>({funcLambda}, "test");
  builder.setInsertPoint(funcLambda);
  auto arg0 = funcLambda->getArg(0);
  auto arg1 = funcLambda->getArg(1);
  // 比较测试
  auto *cond0 = builder.createAndGetRet<pl::GT>({arg0, arg1});
  // if链
  auto *ifLambda = Lambda::get();
  auto *ifBlock = builder.createAndGetRet<pl::If>({cond0, ifLambda});
  auto *elifLambda = Lambda::get();
  auto *cond1 = builder.createAndGetRet<pl::And>({cond0, cond0});
  auto *cond2 = builder.createAndGetRet<pl::Or>({cond0, cond1});
  auto *elifBlock = builder.createAndGetRet<pl::ElIf>({cond2, ifBlock, elifLambda});
  auto *elLambda = Lambda::get();
  auto *elBlock = builder.createAndGetRet<pl::El>({elifBlock, elLambda});
  // 设置参数名
  funcLambda->getArg(0)->setInfo("x");
  // 打印
  std::cout << module->print() << "\n";
  // 现在试试链表能不能用

  // 遍历if链
  auto *cbProto = ifBlock->getProto<pl::CondBlockProto>();
  std::cout << "if block list:\n";
  size_t idx = 0;
  while (cbProto) {
    std::cout << idx++ << ":";
    std::cout << cbProto->getLambda()->print() << "\n";
    cbProto = cbProto->getNext();
  }
}
